<template>
  <el-form
    ref="form"
    class="basic-form"
    :class="className"
    :model="formData"
    :rules="rules"
    :label-width="getLabelWidth()"
    @submit.stop.prevent
  >
    <el-row type="flex" class="flex-wrap" :gutter="10">
      <el-col
        v-for="(item) in getColumns()"
        :key="item.prop"
        :class="item.colClassName"
        :span="item.span || 23"
      >
        <el-form-item
          v-bind="item"
          :label="colon && item.label ? `${item.label}：` : item.label"
          :rules="getRules(item)"
          :label-width="getItemWidth(item)"
          :class="item.className"
        >
          <!-- solt -->
          <template v-if="item.type === 'slot'">
            <slot :name="item.prop" />
          </template>
          <!-- 文本 -->
          <template v-if="item.type === 'text'">
            <span :style="item.style" class="default-text">{{ getContent(item) }}</span>
          </template>
          <!-- 普通输入框 -->
          <el-input
            v-if="item.type === 'input'"
            v-model.trim="formData[item.prop]"
            :type="item.inputType || 'text'"
            :disabled="item.disabled"
            :placeholder="getPlaceholder(item)"
            :min="item.min"
            :max="item.max"
            :maxlength="item.maxlength || customMaxlength"
            clearable
            @input="onInput(item, $event)"
            @change="handleEvent(item)"
          >
            <template #append v-if="item.append">{{ item.append }}</template>
          </el-input>
          <!-- 数字输入框 -->
          <!-- type="number" -->
          <el-input
            v-if="item.type === 'only-number'"
            v-model.trim="formData[item.prop]"
            :disabled="item.disabled"
            :placeholder="getPlaceholder(item)"
            :min="item.min"
            :max="item.max"
            :maxlength="item.maxlength || customMaxlength"
            class="only-number"
            clearable
            @input="onNumberInput(item, $event)"
            @change="handleEvent(item)"
          >
            <template #append v-if="item.append">{{ item.append }}</template>
          </el-input>
          <el-input
            v-if="item.type === 'price'"
            v-model.trim="formData[item.prop]"
            :disabled="item.disabled"
            :placeholder="getPlaceholder(item)"
            :min="item.min"
            :max="item.max"
            :maxlength="item.maxlength || customMaxlength"
            class="only-number"
            clearable
            @input="onPriceInput(item, $event)"
            @change="handleEvent(item)"
          >
            <template #append v-if="item.append">{{ item.append }}</template>
          </el-input>
          <!-- 文本域输入框 -->
          <el-input
            v-if="item.type === 'textarea'"
            v-model.trim="formData[item.prop]"
            :type="item.type"
            :disabled="item.disabled"
            :placeholder="getPlaceholder(item)"
            :autosize="{minRows: 2, maxRows: 10}"
            clearable
            @change="handleEvent(item)"
          >
            <template #append v-if="item.append">{{ item.append }}</template>
          </el-input>
          <!-- 计数器 -->
          <el-input-number
            v-if="item.type === 'inputNumber'"
            v-model="formData[item.prop]"
            :min="item.min"
            :max="item.max"
            :controls="item.controls"
            :step="item.step || 1"
            :precision="item.precision || 0"
            :placeholder="getPlaceholder(item)"
            style="width: 100%"
            @change="handleEvent(item)"
          />
          <!-- 选择框 -->
          <el-select
            v-if="item.type === 'select'"
            v-model="formData[item.prop]"
            :disabled="item.disabled || false"
            :clearable="item.clearable || true"
            :filterable="item.filterable || false"
            :multiple="item.multiple || false"
            :collapseTags="item.collapseTags || false"
            :placeholder="getPlaceholder(item)"
            style="width: 100%"
            @change="handleSelectChange(item)"
          >
            <el-option
              v-for="(childItem) in item.list"
              :key="childItem[item.key || item.valueKey || 'value']"
              :label="childItem[item.labelKey || 'label']"
              :value="childItem[item.valueKey || 'value']"
            />
          </el-select>
          <el-select-v2
            v-if="item.type === 'virtual-select'"
            v-model="formData[item.prop]"
            :options="getVirtualSelectOptions(item)"
            :disabled="item.disabled || false"
            :clearable="item.clearable || true"
            :filterable="item.filterable || false"
            :multiple="item.multiple || false"
            :collapseTags="item.collapseTags || false"
            :placeholder="getPlaceholder(item)"
            style="width: 100%"
            @change="handleSelectChange(item)"
          >
          </el-select-v2>
          <el-radio-group
            v-if="item.type === 'radio'"
            v-model="formData[item.prop]"
            @change="handleEvent(item)"
          >
            <el-radio
              v-for="radio in item.list"
              :key="radio.label"
              :disabled="radio.disabled"
              :label="radio.value"
            >
              {{ radio.label }}
            </el-radio>
          </el-radio-group>
          <!-- 日期选择框 -->
          <el-date-picker
            v-if="item.type === 'date'"
            v-model="formData[item.prop]"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            :type="item.dateType"
            :shortcuts="item.shortcuts"
            :clearable="item.clearable || true"
            :disabled="item.disabled"
            :disabledDate="item.disabledDate"
            :value-format="item.valueFormat || 'YYYY-MM-DD HH:mm:ss'"
            :placeholder="getPlaceholder(item)"
            :popper-class="item.popperClass"
            style="width: 100%"
            :default-time="getDefaultTime(item)"
            @change="handleEvent(item)"
            @clear="handleEvent(item)"
          />
          <el-button
            v-bind="$attrs"
            v-if="item.type === 'search' || item.type === 'btn'"
            :type="item.btnType || 'primary'"
            @click="handleSingleButtonSearch(item)"
          >
            {{ item.text || '搜索' }}
          </el-button>
          <div v-if="item.type === 'btns'" class="flex">
            <template v-for="(btn, index) in item.btns" :key="index">
              <slot v-if="!btn.slot">
                <el-button
                  :type="btn.type"
                  :disabled="btn.disabled"
                  @click="handleMultipleButtonSearch(btn)"
                >
                  {{ btn.text || '搜索' }}
                </el-button>
              </slot>
              <slot v-else :name="btn.prop"></slot>
            </template>
          </div>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>

<script>
import { defineComponent, inject, reactive, ref, toRefs, watch } from 'vue';
import { configProviderContextKey } from '../../hooks/keys.js';
import { useGlobalConfig } from '../../hooks/use-global.config';

const phoneReg = /^1[3-9]\d{9}/;

export default defineComponent({
  name: 'BasicForm',
  props: {
    columns: {
      type: Array,
      default: () => ([]),
    },
    modelValue: {
      type: Object,
      default: () => ({}),
    },
    className: String,
    labelWidth: {
      type: [String, Number],
      default: '110px',
    },
    colon: Boolean,
    rules: Object,
    needSearch: Boolean,
  },
  setup(props, { emit }) {
    const state = reactive({
      formData: {},
    });
    const globalConfig = useGlobalConfig(configProviderContextKey);
    const customMaxlength = ref(globalConfig.value?.maxlength || 50);

    watch(
      () => props.modelValue,
      obj => {
        state.formData = obj;
      },
      {
        immediate: true,
        deep: true,
      },
    );
    const getVirtualSelectOptions = (item = {}) => {
      if (!item.list || !item.list.length) return [];
      return item.list.map(row => ({ ...row, label: row[item.labelKey || 'label'], value: row[item.valueKey || 'value'] }));
    };
    const getLabelWidth = () => {
      if (!props.labelWidth.endsWith('px')) {
        return `${props.labelWidth}px`;
      }
      return props.labelWidth;
    };
    const getItemWidth = (item) => {
      if (item.labelWidth) return item.labelWidth;
      if (item.label) return props.labelWidth;
      return '10px';
    };
    const getColumns = () => props.columns;
    const getContent = (item) => {
      if (item.format) {
        return item.format(item);
      }
      return item.unit ? (state.formData[item.prop] || '') + item.unit : state.formData[item.prop];
    };
    const getDefaultTime = (item) => {
      if (item.defaultTime) return item.defaultTime;
      if (item.dateType.includes('range')) return [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)];
      return new Date(2000, 1, 1);
    };
    const getPlaceholder = (item) => {
      if (item.placeholder) return item.placeholder;
      if (item.type === 'select' || item.type === 'virtual-select' || item.type === 'date') {
        return '请选择';
      }
      return '请输入';
    };
    const handleSelectChange = (item) => {
      if (item.onChange) item.onChange(state.formData[item.prop]);
      else handleEvent(item);
    };
    const handleEvent = (item) => {
      if (item.needSearch) handleSearch(item);
    };
    // 如果是type === 'btn'
    const handleSingleButtonSearch = (item) => {
      if (item.type === 'btn' && item.onClick) item.onClick();
      else handleSearch(item);
    };
    // 如果是type === 'btns'
    const handleMultipleButtonSearch = (btn) => {
      if (btn.custom) {
        if (btn.onClick) btn.onClick();
        return;
      }
      handleSearch(btn);
    };
    const handleSearch = (item) => {
      emit('handleEvent', item);
    };

    const onNumberInput = (item, value) => {
      state.formData[item.prop] = state.formData[item.prop].replace(/[^\d]/g, '');
    };

    const onPriceInput = (item, value) => {
      // 只能输入小数且只能一位小数点
      state.formData[item.prop] = state.formData[item.prop].replace(/[^\d.]/g, '')
        .replace(/\.{2,}/g, '.')
        .replace('.', '$#$')
        .replace(/\./g, '')
        .replace('$#$', '.')
        .replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3')
        .replace(/^\./g, '');
    };

    const onInput = (item, value) => {
      if (value.length > (item.maxlength || customMaxlength.value)) {
        state.formData[item.prop] = value.slice(0, (item.maxlength || customMaxlength.value));
      }
    };

    const validatePhone = (item, value, cb) => {
      if (state.formData[item.prop] && state.formData[item.prop].length !== 11) {
        return cb(new Error('手机号码长度必须为11位数'));
      }
      if (state.formData[item.prop] && !phoneReg.test(state.formData[item.prop])) {
        return cb(new Error('手机号码格式不正确'));
      }
      return cb();
    };
    const getRules = (item) => {
      if (item.validateType === 'phone') {
        return [
          ...(item.rules || []),
          { trigger: 'blur', validator: (rule, value, cb) => validatePhone(item, value, cb) },
          ...(item.required ? [{
            required: item.required,
            message: getPlaceholder(item),
          }] : []),
        ];
      }
      return [
        ...(item.rules || []),
        ...(item.required && !item.rules ? [{
          required: item.required,
          message: getPlaceholder(item),
        }] : []),
      ];
    };

    return {
      // data
      ...toRefs(state),
      customMaxlength,

      // methods
      getVirtualSelectOptions,
      getLabelWidth,
      getItemWidth,
      getColumns,
      getContent,
      getPlaceholder,
      getDefaultTime,
      handleSelectChange,
      handleEvent,
      handleSingleButtonSearch,
      handleMultipleButtonSearch,
      handleSearch,
      onInput,
      onNumberInput,
      onPriceInput,
      validatePhone,
      getRules,
    };
  },
  emits: [
    'handleEvent',
    'update:modelValue',
  ],
  methods: {
  },
});
</script>

<style lang="less">
.basic-form {
  .default-text {
    color: #333;
  }
  [type="btns"] .el-form-item__content {
    flex-wrap: nowrap;
  }
  [type="btns"].flex-end .el-form-item__content {
    justify-content: flex-end;
  }
  .el-form-item {
    margin-right: 0;
    width: 100%;
  }
  // 修复输入中文后光标上移bug
  .only-number {
    .el-input__inner {
      line-height: 1px;
    }
  }
}
</style>
